/*
selectivizr v1.0.3b - (c) Keith Clark, freely distributable under the terms
of the MIT license.
selectivizr.com
*/
/*
Notes about this source
-----------------------
* The #DEBUG_START and #DEBUG_END comments are used to mark blocks of code
that will be removed prior to building a final release version (using a
pre-compression script)
References:
-----------
* CSS Syntax : http://www.w3.org/TR/2003/WD-css3-syntax-20030813/#style
* Selectors : http://www.w3.org/TR/css3-selectors/#selectors
* IE Compatability : http://msdn.microsoft.com/en-us/library/cc351024(VS.85).aspx
* W3C Selector Tests : http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/html/tests/
*/
(function(win) {
// Determine IE version and stop execution if browser isn't IE. This
// handles the script being loaded by non IE browsers because the
// developer didn't use conditional comments.
var ieUserAgent = navigator.userAgent.match(/MSIE (\d+)/);
if (!ieUserAgent) {
return false;
}
// =========================== Init Objects ============================
var doc = document;
var root = doc.documentElement;
var xhr = getXHRObject();
var ieVersion = ieUserAgent[1];
// If were not in standards mode, IE is too old / new or we can't create
// an XMLHttpRequest object then we should get out now.
if (doc.compatMode != 'CSS1Compat' || ieVersion<6 || ieVersion>8 || !xhr) {
return;
}
// ========================= Common Objects ============================
// Compatiable selector engines in order of CSS3 support. Note: '*' is
// a placholder for the object key name. (basically, crude compression)
var selectorEngines = {
"NW"	: "*.Dom.select",
"MooTools"	: "$$",
"DOMAssistant"	: "*.$",
"Prototype"	: "$$",
"YAHOO"	: "*.util.Selector.query",
"Sizzle"	: "*",
"jQuery"	: "*",
"dojo"	: "*.query"
};
var selectorMethod;
var enabledWatchers = []; // array of :enabled/:disabled elements to poll
var domPatches = [];
var ie6PatchID = 0; // used to solve ie6's multiple class bug
var patchIE6MultipleClasses = true; // if true adds class bloat to ie6
var namespace = "slvzr";
// Stylesheet parsing regexp's
var RE_COMMENT = /(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)\s*?/g;
var RE_IMPORT = /@import\s*(?:(?:(?:url\(\s*(['"]?)(.*)\1)\s*\))|(?:(['"])(.*)\3))\s*([^;]*);/g;
var RE_ASSET_URL = /(behavior\s*?:\s*)?\burl\(\s*(["']?)(?!data:)([^"')]+)\2\s*\)/g;
var RE_PSEUDO_STRUCTURAL = /^:(empty|(first|last|only|nth(-last)?)-(child|of-type))$/;
var RE_PSEUDO_ELEMENTS = /:(:first-(?:line|letter))/g;
var RE_SELECTOR_GROUP = /((?:^|(?:\s*})+)(?:\s*@media[^{]+{)?)\s*([^\{]*?[\[:][^{]+)/g;
var RE_SELECTOR_PARSE = /([ +~>])|(:[a-z-]+(?:\(.*?\)+)?)|(\[.*?\])/g;
var RE_LIBRARY_INCOMPATIBLE_PSEUDOS = /(:not\()?:(hover|enabled|disabled|focus|checked|target|active|visited|first-line|first-letter)\)?/g;
var RE_PATCH_CLASS_NAME_REPLACE = /[^\w-]/g;
// HTML UI element regexp's
var RE_INPUT_ELEMENTS = /^(INPUT|SELECT|TEXTAREA|BUTTON)$/;
var RE_INPUT_CHECKABLE_TYPES = /^(checkbox|radio)$/;
// Broken attribute selector implementations (IE7/8 native [^=""], [$=""] and [*=""])
var BROKEN_ATTR_IMPLEMENTATIONS = ieVersion>6 ? /[\$\^*]=(['"])\1/ : null;
// Whitespace normalization regexp's
var RE_TIDY_TRAILING_WHITESPACE = /([(\[+~])\s+/g;
var RE_TIDY_LEADING_WHITESPACE = /\s+([)\]+~])/g;
var RE_TIDY_CONSECUTIVE_WHITESPACE = /\s+/g;
var RE_TIDY_TRIM_WHITESPACE = /^\s*((?:[\S\s]*\S)?)\s*$/;
// String constants
var EMPTY_STRING = "";
var SPACE_STRING = " ";
var PLACEHOLDER_STRING = "$1";
// =========================== Patching ================================
// --[ patchStyleSheet() ]----------------------------------------------
// Scans the passed cssText for selectors that require emulation and
// creates one or more patches for each matched selector.
function patchStyleSheet( cssText ) {
return cssText.replace(RE_PSEUDO_ELEMENTS, PLACEHOLDER_STRING).
replace(RE_SELECTOR_GROUP, function(m, prefix, selectorText) {
var selectorGroups = selectorText.split(",");
for (var c = 0, cs = selectorGroups.length; c < cs; c++) {
var selector = normalizeSelectorWhitespace(selectorGroups[c]) + SPACE_STRING;
var patches = [];
selectorGroups[c] = selector.replace(RE_SELECTOR_PARSE,
function(match, combinator, pseudo, attribute, index) {
if (combinator) {
if (patches.length>0) {
domPatches.push( { selector: selector.substring(0, index), patches: patches } )
patches = [];
}
return combinator;
}
else {
var patch = (pseudo) ? patchPseudoClass( pseudo ) : patchAttribute( attribute );
if (patch) {
patches.push(patch);
return "." + patch.className;
}
return match;
}
}
);
}
return prefix + selectorGroups.join(",");
});
};
// --[ patchAttribute() ]-----------------------------------------------
// returns a patch for an attribute selector.
function patchAttribute( attr ) {
return (!BROKEN_ATTR_IMPLEMENTATIONS || BROKEN_ATTR_IMPLEMENTATIONS.test(attr)) ?
{ className: createClassName(attr), applyClass: true } : null;
};
// --[ patchPseudoClass() ]---------------------------------------------
// returns a patch for a pseudo-class
function patchPseudoClass( pseudo ) {
var applyClass = true;
var className = createClassName(pseudo.slice(1));
var isNegated = pseudo.substring(0, 5) == ":not(";
var activateEventName;
var deactivateEventName;
// if negated, remove :not()
if (isNegated) {
pseudo = pseudo.slice(5, -1);
}
// bracket contents are irrelevant - remove them
var bracketIndex = pseudo.indexOf("(")
if (bracketIndex > -1) {
pseudo = pseudo.substring(0, bracketIndex);
}
// check we're still dealing with a pseudo-class
if (pseudo.charAt(0) == ":") {
switch (pseudo.slice(1)) {
case "root":
applyClass = function(e) {
return isNegated ? e != root : e == root;
}
break;
case "target":
// :target is only supported in IE8
if (ieVersion == 8) {
applyClass = function(e) {
var handler = function() {
var hash = location.hash;
var hashID = hash.slice(1);
return isNegated ? (hash == EMPTY_STRING || e.id != hashID) : (hash != EMPTY_STRING && e.id == hashID);
};
addEvent( win, "hashchange", function() {
toggleElementClass(e, className, handler());
})
return handler();
}
break;
}
return false;
case "checked":
applyClass = function(e) {
if (RE_INPUT_CHECKABLE_TYPES.test(e.type)) {
addEvent( e, "propertychange", function() {
if (event.propertyName == "checked") {
toggleElementClass( e, className, e.checked !== isNegated );
}
})
}
return e.checked !== isNegated;
}
break;
case "disabled":
isNegated = !isNegated;
case "enabled":
applyClass = function(e) {
if (RE_INPUT_ELEMENTS.test(e.tagName)) {
addEvent( e, "propertychange", function() {
if (event.propertyName == "$disabled") {
toggleElementClass( e, className, e.$disabled === isNegated );
}
});
enabledWatchers.push(e);
e.$disabled = e.disabled;
return e.disabled === isNegated;
}
return pseudo == ":enabled" ? isNegated : !isNegated;
}
break;
case "focus":
activateEventName = "focus";
deactivateEventName = "blur";
case "hover":
if (!activateEventName) {
activateEventName = "mouseenter";
deactivateEventName = "mouseleave";
}
applyClass = function(e) {
addEvent( e, isNegated ? deactivateEventName : activateEventName, function() {
toggleElementClass( e, className, true );
})
addEvent( e, isNegated ? activateEventName : deactivateEventName, function() {
toggleElementClass( e, className, false );
})
return isNegated;
}
break;
// everything else
default:
// If we don't support this pseudo-class don't create
// a patch for it
if (!RE_PSEUDO_STRUCTURAL.test(pseudo)) {
return false;
}
break;
}
}
return { className: className, applyClass: applyClass };
};
// --[ applyPatches() ]-------------------------------------------------
function applyPatches() {
var elms, selectorText, patches, domSelectorText;
for (var c=0; c<domPatches.length; c++) {
selectorText = domPatches[c].selector;
patches = domPatches[c].patches;
// Although some selector libraries can find :checked :enabled etc.
// we need to find all elements that could have that state because
// it can be changed by the user.
domSelectorText = selectorText.replace(RE_LIBRARY_INCOMPATIBLE_PSEUDOS, EMPTY_STRING);
// If the dom selector equates to an empty string or ends with
// whitespace then we need to append a universal selector (*) to it.
if (domSelectorText == EMPTY_STRING || domSelectorText.charAt(domSelectorText.length - 1) == SPACE_STRING) {
domSelectorText += "*";
}
// Ensure we catch errors from the selector library
try {
elms = selectorMethod( domSelectorText );
} catch (ex) {
// #DEBUG_START
log( "Selector '" + selectorText + "' threw exception '" + ex + "'" );
// #DEBUG_END
}
if (elms) {
for (var d = 0, dl = elms.length; d < dl; d++) {
var elm = elms[d];
var cssClasses = elm.className;
for (var f = 0, fl = patches.length; f < fl; f++) {
var patch = patches[f];
if (!hasPatch(elm, patch)) {
if (patch.applyClass && (patch.applyClass === true || patch.applyClass(elm) === true)) {
cssClasses = toggleClass(cssClasses, patch.className, true );
}
}
}
elm.className = cssClasses;
}
}
}
};
// --[ hasPatch() ]-----------------------------------------------------
// checks for the exsistence of a patch on an element
function hasPatch( elm, patch ) {
return new RegExp("(^|\\s)" + patch.className + "(\\s|$)").test(elm.className);
};
// =========================== Utility =================================
function createClassName( className ) {
return namespace + "-" + ((ieVersion == 6 && patchIE6MultipleClasses) ?
ie6PatchID++
:
className.replace(RE_PATCH_CLASS_NAME_REPLACE, function(a) { return a.charCodeAt(0) }));
};
// --[ log() ]----------------------------------------------------------
// #DEBUG_START
function log( message ) {
if (win.console) {
win.console.log(message);
}
};
// #DEBUG_END
// --[ trim() ]---------------------------------------------------------
// removes leading, trailing whitespace from a string
function trim( text ) {
return text.replace(RE_TIDY_TRIM_WHITESPACE, PLACEHOLDER_STRING);
};
// --[ normalizeWhitespace() ]------------------------------------------
// removes leading, trailing and consecutive whitespace from a string
function normalizeWhitespace( text ) {
return trim(text).replace(RE_TIDY_CONSECUTIVE_WHITESPACE, SPACE_STRING);
};
// --[ normalizeSelectorWhitespace() ]----------------------------------
// tidies whitespace around selector brackets and combinators
function normalizeSelectorWhitespace( selectorText ) {
return normalizeWhitespace(selectorText.
replace(RE_TIDY_TRAILING_WHITESPACE, PLACEHOLDER_STRING).
replace(RE_TIDY_LEADING_WHITESPACE, PLACEHOLDER_STRING)
);
};
// --[ toggleElementClass() ]-------------------------------------------
// toggles a single className on an element
function toggleElementClass( elm, className, on ) {
var oldClassName = elm.className;
var newClassName = toggleClass(oldClassName, className, on);
if (newClassName != oldClassName) {
elm.className = newClassName;
elm.parentNode.className += EMPTY_STRING;
}
};
// --[ toggleClass() ]--------------------------------------------------
// adds / removes a className from a string of classNames. Used to
// manage multiple class changes without forcing a DOM redraw
function toggleClass( classList, className, on ) {
var re = RegExp("(^|\\s)" + className + "(\\s|$)");
var classExists = re.test(classList);
if (on) {
return classExists ? classList : classList + SPACE_STRING + className;
} else {
return classExists ? trim(classList.replace(re, PLACEHOLDER_STRING)) : classList;
}
};
// --[ addEvent() ]-----------------------------------------------------
function addEvent(elm, eventName, eventHandler) {
elm.attachEvent("on" + eventName, eventHandler);
};
// --[ getXHRObject() ]-------------------------------------------------
function getXHRObject() {
if (win.XMLHttpRequest) {
return new XMLHttpRequest;
}
try	{
return new ActiveXObject('Microsoft.XMLHTTP');
} catch(e) {
return null;
}
};
// --[ loadStyleSheet() ]-----------------------------------------------
function loadStyleSheet( url ) {
xhr.open("GET", url, false);
xhr.send();
return (xhr.status==200) ? xhr.responseText : EMPTY_STRING;
};
// --[ resolveUrl() ]---------------------------------------------------
// Converts a URL fragment to a fully qualified URL using the specified
// context URL. Returns null if same-origin policy is broken
function resolveUrl( url, contextUrl, ignoreSameOriginPolicy ) {
function getProtocol( url ) {
return url.substring(0, url.indexOf("//"));
};
function getProtocolAndHost( url ) {
return url.substring(0, url.indexOf("/", 8));
};
if (!contextUrl) {
contextUrl = baseUrl;
}
// protocol-relative path
if (url.substring(0,2)=="//") {
url = getProtocol(contextUrl) + url;
}
// absolute path
if (/^https?:\/\//i.test(url)) {
return !ignoreSameOriginPolicy && getProtocolAndHost(contextUrl) != getProtocolAndHost(url) ? null : url ;
}
// root-relative path
if (url.charAt(0)=="/") {
return getProtocolAndHost(contextUrl) + url;
}
// relative path
var contextUrlPath = contextUrl.split(/[?#]/)[0]; // ignore query string in the contextUrl
if (url.charAt(0) != "?" && contextUrlPath.charAt(contextUrlPath.length - 1) != "/") {
contextUrlPath = contextUrlPath.substring(0, contextUrlPath.lastIndexOf("/") + 1);
}
return contextUrlPath + url;
};
// --[ parseStyleSheet() ]----------------------------------------------
// Downloads the stylesheet specified by the URL, removes it's comments
// and recursivly replaces @import rules with their contents, ultimately
// returning the full cssText.
function parseStyleSheet( url ) {
if (url) {
return loadStyleSheet(url).replace(RE_COMMENT, EMPTY_STRING).
replace(RE_IMPORT, function( match, quoteChar, importUrl, quoteChar2, importUrl2, media ) {
var cssText = parseStyleSheet(resolveUrl(importUrl || importUrl2, url));
return (media) ? "@media " + media + " {" + cssText + "}" : cssText;
}).
replace(RE_ASSET_URL, function( match, isBehavior, quoteChar, assetUrl ) {
quoteChar = quoteChar || EMPTY_STRING;
return isBehavior ? match : " url(" + quoteChar + resolveUrl(assetUrl, url, true) + quoteChar + ") ";
});
}
return EMPTY_STRING;
};
// --[ getStyleSheets() ]-----------------------------------------------
function getStyleSheets() {
var url, stylesheet;
for (var c = 0; c < doc.styleSheets.length; c++) {
stylesheet = doc.styleSheets[c];
if (stylesheet.href != EMPTY_STRING) {
url = resolveUrl(stylesheet.href);
if (url) {
stylesheet.cssText = stylesheet["rawCssText"] = patchStyleSheet( parseStyleSheet( url ) );
}
}
}
};
// --[ init() ]---------------------------------------------------------
function init() {
applyPatches();
// :enabled & :disabled polling script (since we can't hook
// onpropertychange event when an element is disabled)
if (enabledWatchers.length > 0) {
setInterval( function() {
for (var c = 0, cl = enabledWatchers.length; c < cl; c++) {
var e = enabledWatchers[c];
if (e.disabled !== e.$disabled) {
if (e.disabled) {
e.disabled = false;
e.$disabled = true;
e.disabled = true;
}
else {
e.$disabled = e.disabled;
}
}
}
}, 250)
}
};
// Determine the baseUrl and download the stylesheets
var baseTags = doc.getElementsByTagName("BASE");
var baseUrl = (baseTags.length > 0) ? baseTags[0].href : doc.location.href;
getStyleSheets();
// Bind selectivizr to the ContentLoaded event.
ContentLoaded(win, function() {
// Determine the "best fit" selector engine
for (var engine in selectorEngines) {
var members, member, context = win;
if (win[engine]) {
members = selectorEngines[engine].replace("*", engine).split(".");
while ((member = members.shift()) && (context = context[member])) {}
if (typeof context == "function") {
selectorMethod = context;
init();
return;
}
}
}
});
/*!
* ContentLoaded.js by Diego Perini, modified for IE<9 only (to save space)
*
* Author: Diego Perini (diego.perini at gmail.com)
* Summary: cross-browser wrapper for DOMContentLoaded
* Updated: 20101020
* License: MIT
* Version: 1.2
*
* URL:
* http://javascript.nwbox.com/ContentLoaded/
* http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
*
*/
// @w window reference
// @f function reference
function ContentLoaded(win, fn) {
var done = false, top = true,
init = function(e) {
if (e.type == "readystatechange" && doc.readyState != "complete") return;
(e.type == "load" ? win : doc).detachEvent("on" + e.type, init, false);
if (!done && (done = true)) fn.call(win, e.type || e);
},
poll = function() {
try { root.doScroll("left"); } catch(e) { setTimeout(poll, 50); return; }
init('poll');
};
if (doc.readyState == "complete") fn.call(win, EMPTY_STRING);
else {
if (doc.createEventObject && root.doScroll) {
try { top = !win.frameElement; } catch(e) { }
if (top) poll();
}
addEvent(doc,"readystatechange", init);
addEvent(win,"load", init);
}
};
})(this);